package com.bdqn.qnnews.schedule.job;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.bdqn.qnnews.common.constants.RedisConstants;
import com.bdqn.qnnews.schedule.api.dto.TaskDto;
import com.bdqn.qnnews.schedule.convert.TaskConvert;
import com.bdqn.qnnews.schedule.entity.Taskinfo;
import com.bdqn.qnnews.schedule.mapper.TaskinfoMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author xlzhang
 * @date 2024/1/17 8:49
 */
@Slf4j
@RequiredArgsConstructor
@Component
public class SyncDBToCacheJob {

    private final TaskinfoMapper taskinfoMapper;
    private final StringRedisTemplate redisTemplate;
    private final RedissonClient redissonClient;

    // 代表每隔5分钟自动执行一次任务
    @Scheduled(cron = "0 */5 * * * ?")
    public void syncData() throws InterruptedException {
        // 添加分布式锁，实现多个服务同时开启定时任务的互斥问题
        final RLock lock = redissonClient.getLock("sync-data-tesk-key");
        // 不写默认30秒自动释放
        // 注意这个时间必须小于定时任务间隔时间，否则定时任务会被中断
        final boolean res = lock.tryLock(0,3, TimeUnit.MINUTES);
        if (!res){
            System.out.println("获取锁失败，任务中断: " + res);
            return;
        }

        log.debug("执行同步MySQL数据到缓存任务，{}", LocalDateTime.now());
        // 查询MySQL符合条件（未来5分钟内执行）的任务
        // 创建一个未来5分钟的日期对象
        final LocalDateTime offset = LocalDateTimeUtil
                .offset(LocalDateTime.now(), 5, ChronoUnit.MINUTES);
        final LambdaQueryWrapper<Taskinfo> wrapper =
                Wrappers.<Taskinfo>lambdaQuery().lt(Taskinfo::getExecuteTime, offset);
        // 执行查询
        final List<Taskinfo> taskinfoList = taskinfoMapper.selectList(wrapper);

        if (CollectionUtil.isNotEmpty(taskinfoList)){
            taskinfoList.forEach(taskinfo -> {
                // 转换数据类型
                final TaskDto taskDto = TaskConvert.INSTANCE.entityToDto(taskinfo);
                // 设置发布时间为时间搓
                taskDto.setExecuteTime(LocalDateTimeUtil.toEpochMilli(taskinfo.getExecuteTime()));
                // 保存到redis
                // 1. 创建一个key，用来保存数据
                String redisKey = RedisConstants.TASK_TOPIC_PREFIX + taskDto.getTaskTopic();
            /*
                2. 保存数据到redis
                参数： 1. 缓存队列的名称(key) 2. 要缓存的任务数据  3. score需要使用发布时间的毫秒值
             */
                redisTemplate.opsForZSet()
                        .add(redisKey, JSONUtil.toJsonStr(taskDto), taskDto.getExecuteTime());
            });
        }

        log.debug("执行同步MySQL数据完成：{}",
                taskinfoList.stream()
                .map(Taskinfo::getTaskId)
                .collect(Collectors.toList()));
    }
}
